package com.ruoyi.flow.common.tools.mediaconverter.converter.command;

import static com.google.common.base.Preconditions.checkNotNull;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeoutException;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * Created by Administrator on 2017/7/14.
 */
public class CommandInvoker {
    private static final Logger LOGGER = LoggerFactory.getLogger(CommandInvoker.class);
    /**
     * 参数
     */
    private List<String> args = new ArrayList<String>();

    private List<String> stdInputString;

    private CommandKiller commandKiller;

    private Process process;

    private String command;
    private String directory;

    public List<String> getStdInputString() {
        return stdInputString;
    }

    public CommandInvoker(String command, String directory, List<String> args) {
        checkNotNull(command);
        checkNotNull(args);
        checkNotNull(directory);
        this.command = command;
        this.directory = directory;
        this.args.addAll(args);
    }

    public CommandInvoker(String command, String directory) {
        checkNotNull(command);
        checkNotNull(directory);
        this.directory = directory;
        this.command = command;
    }

    public CommandInvoker(String command) {
        checkNotNull(command);
        this.command = command;
    }

    /**
     * 添加脚本参数
     *
     * @param arg
     * @return
     */
    public CommandInvoker addArg(String arg) {
        checkNotNull(arg);
        if (!args.contains(arg)) {
            args.add(arg);
        }
        return this;
    }

    /**
     * 添加多个脚本参数
     *
     * @param pargs
     * @return
     */
    public CommandInvoker addArgs(String... pargs) {
        if (pargs.length > 0) {
            for (String arg : pargs) {
                if (!args.contains(arg)) {
                    args.add(arg);
                }
            }
        }
        return this;
    }

    /**
     * 执行脚本，不设置超时时间，会阻塞等待至任务完成
     *
     * @return
     * @throws IOException
     * @throws InterruptedException
     * @throws TimeoutException
     */
    public boolean invoke() throws IOException, InterruptedException,TimeoutException {
        return invoke(-1);
    }

    /**
     * 执行脚本
     *
     * @param timeout 脚本执行超时时间
     * @return
     * @throws IOException
     * @throws InterruptedException
     * @throws TimeoutException
     */
    public boolean invoke(long timeout) throws IOException, InterruptedException, TimeoutException {
        ProcessBuilder builder = null;
        args.add(0, this.command);
        builder = new ProcessBuilder(args);
        if (!StringUtils.isEmpty(this.directory)) {
            builder.directory(new File(this.directory));
        }
        builder.redirectErrorStream(true);
        LOGGER.info("begin execute command:{}", args.toString());

        this.process = builder.start();
        commandKiller = new CommandKiller(this.process);

        //结束系统进程时停止底层的调用
        Runtime runtime = Runtime.getRuntime();
        runtime.addShutdownHook(commandKiller);
        CommandWatcher watchThread = new CommandWatcher(process);
        if (timeout == -1) {//不超时
            watchThread.start();
            int runningStatus = process.waitFor();
            watchThread.setOver(true);
            return runningStatus == 0;
        } else {
            CommandWorker worker = new CommandWorker(process,watchThread);
            worker.start();
            try {
                worker.join(timeout);
                if (worker.getExit() != null) {
                    return worker.getExit().intValue() == 0;
                } else {
                    throw new TimeoutException();
                }
            } catch (TimeoutException e) {
                e.printStackTrace();
                worker.interrupt();
                Thread.currentThread().interrupt();
            }
        }
        this.stdInputString = watchThread.getStream();
        return false;
    }

    /**
     * 关闭流
     */
    public void destroy() {
        if (this.process != null) {
            this.process.destroy();
            this.process = null;
        }
        if (this.commandKiller != null) {
            Runtime runtime = Runtime.getRuntime();
            runtime.removeShutdownHook(this.commandKiller);
            this.commandKiller = null;
        }
    }
}
